﻿using Asv.Common;

namespace Asv.Avalonia.Map
{
    /// <summary>
    ///     Plate Carrée (literally, “plane square”) projection
    ///     PROJCS["WGS 84 / World Equidistant
    ///     Cylindrical",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],UNIT["Meter",1]]
    ///     "spatialReference":
    ///     {"wkid":4326},"singleFusedMapCache":true,"tileInfo":
    ///     {"rows":256,"cols":256,"dpi":96,"format":"PNG8","compressionQuality":0,
    ///     "origin":{"x":-400,"y":400},"spatialReference":{"wkid":4326},"lods":
    ///     [{"level":0,"resolution":0.0118973050291514,"scale":5000000},
    ///     {"level":1,"resolution":0.0059486525145757,"scale":2500000},
    ///     {"level":2,"resolution":0.00297432625728785,"scale":1250000},
    ///     {"level":3,"resolution":0.00118973050291514,"scale":500000},
    ///     {"level":4,"resolution":0.00059486525145757,"scale":250000},
    ///     {"level":5,"resolution":0.000356919150874542,"scale":150000},
    ///     {"level":6,"resolution":0.000178459575437271,"scale":75000},
    ///     {"level":7,"resolution":0.000118973050291514,"scale":50000},
    ///     {"level":8,"resolution":5.9486525145757E-05,"scale":25000},
    ///     {"level":9,"resolution":3.56919150874542E-05,"scale":15000},
    ///     {"level":10,"resolution":1.90356880466422E-05,"scale":8000},
    ///     {"level":11,"resolution":9.51784402332112E-06,"scale":4000},
    ///     {"level":12,"resolution":4.75892201166056E-06,"scale":2000}]},
    ///     "initialExtent":
    ///     {"xmin":42.1125196069871,"ymin":18.6650706214551,"xmax":65.698643558112
    ///     4,"ymax":29.4472987133981,"spatialReference":{"wkid":4326}},
    ///     "fullExtent":
    ///     {"xmin":41.522866508209,"ymin":18.7071563263201,"xmax":66.2882966568906
    ///     ,"ymax":29.4052130085331,"spatialReference":{"wkid":4326}},
    ///     "units":"esriDecimalDegrees"
    /// </summary>
    public class PlateCarreeProjectionDarbAe : PureProjection
    {
        public static readonly PlateCarreeProjectionDarbAe Instance = new PlateCarreeProjectionDarbAe();

        public static readonly double MinLatitude = 18.7071563263201;
        public static readonly double MaxLatitude = 29.4052130085331;
        public static readonly double MinLongitude = 41.522866508209;
        public static readonly double MaxLongitude = 66.2882966568906;

        static readonly double orignX = -400;
        static readonly double orignY = 400;

        public override RectLatLng Bounds
        {
            get
            {
                return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude);
            }
        }

        public override GSize TileSize
        {
            get;
        } = new GSize(256, 256);

        public override double Axis
        {
            get
            {
                return 6378137;
            }
        }

        public override double Flattening
        {
            get
            {
                return 1.0 / 298.257223563;
            }
        }

        public override GPoint FromLatLngToPixel(double lat, double lng, int zoom)
        {
            GPoint ret = GPoint.Empty;

            lat = Clip(lat, MinLatitude, MaxLatitude);
            lng = Clip(lng, MinLongitude, MaxLongitude);

            /*
              getContainingTileCoords:function(ti,_1dd,lod)
              {
                var to=ti.origin,
                res=lod.resolution,
                tmw=ti.width*res,
                tmh=ti.height*res,
                tc=Math.floor((_1dd.x-to.x)/tmw),
                tr=Math.floor((to.y-_1dd.y)/tmh);
              }
            */

            double res = GetTileMatrixResolution(zoom);

            ret.X = (long)Math.Floor((lng - orignX) / res);
            ret.Y = (long)Math.Floor((orignY - lat) / res);

            return ret;
        }

        public override GeoPoint FromPixelToLatLng(long x, long y, int zoom)
        {
            double res = GetTileMatrixResolution(zoom);
            return new GeoPoint(orignY - y * res, x * res + orignX,0);
        }

        public static double GetTileMatrixResolution(int zoom)
        {
            double ret = 0;

            switch (zoom)
            {
                #region -- sizes --

                case 0:
                {
                    ret = 0.0118973050291514;
                }
                    break;

                case 1:
                {
                    ret = 0.0059486525145757;
                }
                    break;

                case 2:
                {
                    ret = 0.00297432625728785;
                }
                    break;

                case 3:
                {
                    ret = 0.00118973050291514;
                }
                    break;

                case 4:
                {
                    ret = 0.00059486525145757;
                }
                    break;

                case 5:
                {
                    ret = 0.000356919150874542;
                }
                    break;

                case 6:
                {
                    ret = 0.000178459575437271;
                }
                    break;

                case 7:
                {
                    ret = 0.000118973050291514;
                }
                    break;

                case 8:
                {
                    ret = 5.9486525145757E-05;
                }
                    break;

                case 9:
                {
                    ret = 3.56919150874542E-05;
                }
                    break;

                case 10:
                {
                    ret = 1.90356880466422E-05;
                }
                    break;

                case 11:
                {
                    ret = 9.51784402332112E-06;
                }
                    break;

                case 12:
                {
                    ret = 4.75892201166056E-06;
                }
                    break;

                #endregion
            }

            return ret;
        }

        public override double GetGroundResolution(int zoom, double latitude)
        {
            return GetTileMatrixResolution(zoom);
        }

        public override GSize GetTileMatrixMaxXY(int zoom)
        {
            var maxPx = FromLatLngToPixel(MinLatitude, MaxLongitude, zoom);
            return new GSize(FromPixelToTileXY(maxPx));
        }

        public override GSize GetTileMatrixMinXY(int zoom)
        {
            var minPx = FromLatLngToPixel(MaxLatitude, MinLongitude, zoom);
            return new GSize(FromPixelToTileXY(minPx));
        }
    }
}
